﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Linq;

namespace Gmantis.Controls
{
    public interface IScrollInfo
    {
        // Methods
        void LineDown();
        void LineLeft();
        void LineRight();
        void LineUp();
        Rect MakeVisible(UIElement visual, Rect rectangle);
        void MouseWheelDown();
        void MouseWheelLeft();
        void MouseWheelRight();
        void MouseWheelUp();
        void PageDown();
        void PageLeft();
        void PageRight();
        void PageUp();
        void SetHorizontalOffset(double offset);
        void SetVerticalOffset(double offset);

        // Properties
        bool CanHorizontallyScroll { get; set; }
        bool CanVerticallyScroll { get; set; }
        double ExtentHeight { get; }
        double ExtentWidth { get; }
        double HorizontalOffset { get; }
        System.Windows.Controls.ScrollViewer ScrollOwner { get; set; }
        double VerticalOffset { get; }
        double ViewportHeight { get; }
        double ViewportWidth { get; }
    }

    public class ScrollViewerTouchBehavior : DependencyObject
    {
        // Fields
        private DragDirection _direction;
        private bool _discreteScrolling;
        private DragHelper _dragHelper;
        private double _initialHorizontalOffset;
        private double _initialVerticalOffset;
        private bool _listenDragGesture;
        private FrameworkElement _scrollViewerContent;
        internal const double GESTURE_THRESHOLD = 10.0;
        public static readonly DependencyProperty HasInertiaProperty;
        public static readonly DependencyProperty IgnoreSwipeProperty;
        public static readonly DependencyProperty PanningDirectionProperty;
        public static readonly DependencyProperty TouchBehaviorProperty;

        // Methods
        static ScrollViewerTouchBehavior()
        {
            TouchBehaviorProperty = DependencyProperty.RegisterAttached("TouchBehavior", typeof(ScrollViewerTouchBehavior), typeof(ScrollViewerTouchBehavior),
                new PropertyMetadata(null, delegate(DependencyObject d, DependencyPropertyChangedEventArgs a)
                    {
                        if (a.OldValue != null)
                        {
                            ScrollViewerTouchBehavior oldValue = (ScrollViewerTouchBehavior)a.OldValue;
                            oldValue.OnDetaching();
                            oldValue.ScrollViewer = d as System.Windows.Controls.ScrollViewer;
                        }
                        if (a.NewValue != null)
                        {
                            ScrollViewerTouchBehavior newValue = (ScrollViewerTouchBehavior)a.NewValue;
                            newValue.ScrollViewer = d as System.Windows.Controls.ScrollViewer;
                            newValue.OnAttached();
                        }
                    }));

            HasInertiaProperty = DependencyProperty.Register("HasInertia", typeof(bool), typeof(ScrollViewerTouchBehavior),
                new PropertyMetadata(true, delegate(DependencyObject d, DependencyPropertyChangedEventArgs a)
                    {
                        ScrollViewerTouchBehavior behavior = d as ScrollViewerTouchBehavior;
                        if (behavior.ScrollViewer != null)
                        {
                            behavior.OnDetaching();
                            behavior.OnAttached();
                        }
                    }));

            PanningDirectionProperty = DependencyProperty.Register("PanningDirection", typeof(Orientation), typeof(ScrollViewerTouchBehavior), 
                new PropertyMetadata(Orientation.Vertical));
            IgnoreSwipeProperty = DependencyProperty.Register("IgnoreSwipe", typeof(bool), typeof(ScrollViewerTouchBehavior), 
                new PropertyMetadata(false));
        }

        private void OnAttached()
        {
            FrameworkElement content = this.ScrollViewer.Content as FrameworkElement;
            if (this.ScrollViewer.Content is ItemsPresenter)
            {
                ItemsPresenter node = this.ScrollViewer.Content as ItemsPresenter;
                content = node.GetVisualChildren().FirstOrDefault<FrameworkElement>();
                if (content == null)
                {
                    node.LayoutUpdated += new EventHandler(this.OnItemsPresenterLayoutUpdated);
                }
            }
            if (content != null)
            {
                this.SetScrollViewerContent(content);
            }
        }

        private void OnDetaching()
        {
            if (this._dragHelper != null)
            {
                this._dragHelper.FinalizeDrag();
            }
            if (this.ScrollViewer != null)
            {
                ItemsPresenter content = this.ScrollViewer.Content as ItemsPresenter;
                if (content != null)
                {
                    content.LayoutUpdated -= new EventHandler(this.OnItemsPresenterLayoutUpdated);
                }
            }
            if (this._scrollViewerContent != null)
            {
                this._scrollViewerContent.LayoutUpdated -= new EventHandler(this.OnScrollViewerContentLayoutUpdated);
            }
        }

        private void OnDragDelta(object sender, DragDeltaEventArgs e)
        {
            if (((e.PointerDeviceType == PointerDeviceType.Touch) && (this.ScrollViewer != null)) && (!this.IgnoreSwipe || !this.IsCrossSlide))
            {
                if (this._discreteScrolling)
                {
                    if ((this.ScrollViewer.ActualWidth > 0.0) && (this.ScrollViewer.ActualHeight > 0.0))
                    {
                        double num = this.ScrollViewer.ActualHeight / this.ScrollViewer.ViewportHeight;
                        this.ScrollViewer.ScrollToHorizontalOffset(this._initialHorizontalOffset - e.CumulativeTranslation.X);
                        this.ScrollViewer.ScrollToVerticalOffset(this._initialVerticalOffset - (e.CumulativeTranslation.Y / num));
                    }
                }
                else
                {
                    this.ScrollViewer.ScrollToHorizontalOffset(this._initialHorizontalOffset - e.CumulativeTranslation.X);
                    this.ScrollViewer.ScrollToVerticalOffset(this._initialVerticalOffset - e.CumulativeTranslation.Y);
                }
            }
        }

        private void OnDragStarted(object sender, DragStartedEventArgs e)
        {
            this._direction = e.Direction;
            this._initialHorizontalOffset = this.ScrollViewer.HorizontalOffset;
            this._initialVerticalOffset = this.ScrollViewer.VerticalOffset;
        }

        private void OnItemsPresenterLayoutUpdated(object sender, EventArgs e)
        {
            ItemsPresenter content = this.ScrollViewer.Content as ItemsPresenter;
            if (content != null)
            {
                FrameworkElement scrollViewerContent = content.GetVisualChildren().FirstOrDefault<FrameworkElement>();
                if (scrollViewerContent != null)
                {
                    content.LayoutUpdated -= new EventHandler(this.OnItemsPresenterLayoutUpdated);
                    this.SetScrollViewerContent(scrollViewerContent);
                }
            }
        }

        private void OnScrollViewerContentLayoutUpdated(object sender, EventArgs e)
        {
            bool flag = (this.ScrollViewer.ScrollableWidth > 0.0) || (this.ScrollViewer.ScrollableHeight > 0.0);
            if (this._listenDragGesture != flag)
            {
                this._listenDragGesture = flag;
                if (flag)
                {
                    DragHelperMode mode = DragHelperMode.TranslateRailX | DragHelperMode.TranslateRailY | DragHelperMode.TranslateX | DragHelperMode.TranslateY;
                    if (this.HasInertia)
                    {
                        mode = (DragHelperMode)((byte)(mode | DragHelperMode.Inertia));
                    }
                    bool handledEventsToo = true;
                    this._dragHelper = new DragHelper(this._scrollViewerContent, mode, 10.0, false, handledEventsToo, false, false);
                    this._dragHelper.DragStarted += new EventHandler<DragStartedEventArgs>(this.OnDragStarted);
                    this._dragHelper.DragDelta += new EventHandler<DragDeltaEventArgs>(this.OnDragDelta);
                }
                else
                {
                    this._dragHelper.FinalizeDrag();
                }
            }
        }

        private void SetScrollViewerContent(FrameworkElement scrollViewerContent)
        {
            this._discreteScrolling = scrollViewerContent is IScrollInfo;
            this._scrollViewerContent = scrollViewerContent;
            this._scrollViewerContent.LayoutUpdated += new EventHandler(this.OnScrollViewerContentLayoutUpdated);
        }

        public static ScrollViewerTouchBehavior GetTouchBehavior(System.Windows.Controls.ScrollViewer scrollViewer)
        {
            return (ScrollViewerTouchBehavior)scrollViewer.GetValue(TouchBehaviorProperty);
        }

        public static void SetTouchBehavior(System.Windows.Controls.ScrollViewer scrollViewer, ScrollViewerTouchBehavior value)
        {
            scrollViewer.SetValue(TouchBehaviorProperty, value);
        }

        // Properties
        public bool HasInertia
        {
            get { return (bool)GetValue(HasInertiaProperty); }
            set { SetValue(HasInertiaProperty, value); }
        }

        public bool IgnoreSwipe
        {
            get { return (bool)GetValue(IgnoreSwipeProperty); }
            set { SetValue(IgnoreSwipeProperty, value); }
        }

        private bool IsCrossSlide
        {
            get
            {
                return (((this.PanningDirection == Orientation.Vertical) && (this._direction == DragDirection.Horizontal)) ||
                    ((this.PanningDirection == Orientation.Horizontal) && (this._direction == DragDirection.Vertical)));
            }
        }

        public Orientation PanningDirection
        {
            get { return (Orientation)GetValue(PanningDirectionProperty); }
            set { SetValue(PanningDirectionProperty, value); }
        }

        private System.Windows.Controls.ScrollViewer ScrollViewer { get; set; } 
    }
}
